<!DOCTYPE html>
<title>Web GPU Demo</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- For *.skia.org https://developer.chrome.com/origintrials/#/registration/2983494015644598273
     Expires Nov 19, 2021
     -->
<meta http-equiv="origin-trial" content="AnRs8mYss+Awd1DPUg2VfjXJbw2087/Dysaa3L7JmrbzTkwoEr87cX3y0zUfTGOFSLKJLRqNEmFAwfy+uumVXQsAAABbeyJvcmlnaW4iOiJodHRwczovL3NraWEub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJHUFUiLCJleHBpcnkiOjE2NDMxNTUxOTksImlzU3ViZG9tYWluIjp0cnVlfQ==">

<!-- For localhost:8123 https://developer.chrome.com/origintrials/#/registration/6568359319031513089
     Expires Nov 19, 2021
     -->
<meta http-equiv="origin-trial" content="ArQyw1ckz8lMOAcs5BbhOVJh2A6KMhYL6w/rTjPNnViqZyfFhlyJ5hnuHARoCkS1ZKiJi+YbsFvPWy23ePkFMQgAAABJeyJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjgxMjMiLCJmZWF0dXJlIjoiV2ViR1BVIiwiZXhwaXJ5IjoxNjQzMTU1MTk5fQ==">

<style>
  canvas {
    border: 1px dashed grey;
  }
</style>

<body>
  <h1>WebGPU Test</h1>
  <pre id="log"></pre>

  <canvas id=draw width=500 height=500></canvas>
</body>

<script type="text/javascript" charset="utf-8">
  if ("gpu" in navigator) {
    log("WebGPU detected")
    WebGPUDemo();
  } else {
    log("No WebGPU support.")
  }

  function log(s) {
    document.getElementById("log").innerText = s;
  }

  async function WebGPUDemo() {
    // Adapted from https://github.com/austinEng/webgpu-samples/blob/main/src/sample/helloTriangle/main.ts
    const adapter = await navigator.gpu.requestAdapter();
    if (!adapter) {
      log("Could not load an adapter. For Chrome, try running with --enable-features=Vulkan --enable-unsafe-webgpu");
      return;
    }

    const device = await adapter.requestDevice();
    console.log(adapter, device);
    const canvas = document.getElementById("draw");
    const context = canvas.getContext('webgpu');
    if (!context) {
      log("Could not load webgpu context");
      return;
    }
    console.log(context);

    const devicePixelRatio = window.devicePixelRatio || 1;
    const presentationSize = [
      canvas.clientWidth * devicePixelRatio,
      canvas.clientHeight * devicePixelRatio,
    ];
    const presentationFormat = context.getPreferredFormat(adapter);

    context.configure({
      device,
      format: presentationFormat,
      size: presentationSize,
    });

    const triangleVertWGSL = `[[stage(vertex)]]
fn main([[builtin(vertex_index)]] VertexIndex : u32)
     -> [[builtin(position)]] vec4<f32> {
  var pos = array<vec2<f32>, 3>(
      vec2<f32>(0.0, 0.5),
      vec2<f32>(-0.5, -0.5),
      vec2<f32>(0.5, -0.5));

  return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
}`;

    const redFragWGSL = `[[stage(fragment)]]
fn main() -> [[location(0)]] vec4<f32> {
  return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}`;

    const pipeline = device.createRenderPipeline({
      vertex: {
        module: device.createShaderModule({
          code: triangleVertWGSL,
        }),
        entryPoint: 'main',
      },
      fragment: {
        module: device.createShaderModule({
          code: redFragWGSL,
        }),
        entryPoint: 'main',
        targets: [
          {
            format: presentationFormat,
          },
        ],
      },
      primitive: {
        topology: 'triangle-list',
      },
    });

    console.log(pipeline);

    const startTime = Date.now();
    function frame() {
      const now = Date.now();
      const commandEncoder = device.createCommandEncoder();
      const textureView = context.getCurrentTexture().createView();

      const renderPassDescriptor = {
        colorAttachments: [
          {
            view: textureView,
            loadValue: {
              r: Math.abs(Math.sin((startTime - now) / 500)),
              g: Math.abs(Math.sin((startTime - now) / 600)),
              b: Math.abs(Math.sin((startTime - now) / 700)),
              a: 1.0 },
            storeOp: 'store',
          },
        ],
      };

      const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
      passEncoder.setPipeline(pipeline);
      passEncoder.draw(3, 1, 0, 0);
      passEncoder.endPass();

      device.queue.submit([commandEncoder.finish()]);
      requestAnimationFrame(frame);
    }

    requestAnimationFrame(frame);
  }
</script>